﻿#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
//#include<assert.h>
#include<string.h>
//编译和链接
// c语言代码的文件不能直接运行，在ANSI C的任何一种实现中，存在两个不同的环境。
// 1，翻译环境：在这个环境中源代码被转换成可执行的机器指令（二进制）指令
//    由 编译-  和 链接 两大过程组成，
//           --预处理->编译->汇编
// 过程：.c（原文件） -> 编译器->.obj（目标文件）->链接器->.exe(可执行程序)
//                      编译：生成--编译            链接
// 
// 
// 2，运行（执行）环境：用于执行代码。
// 
// //生成--生成解决方案：可以将.c文件转换成一个可执行的（二进制的）.exe文件



//编译：--预处理->编译->汇编
//预处理（预编译）详解：
// 1.预定义符号
// c语言设置的一些预定义符号（已有特殊含义的符号），可以直接使用。
// __FILE__ //进行编译的源文件
// __LINE__ //文件当前的行号
// __DATE__ //文件被编译的日期
// __TIME__ //文件被编译的时间
// __STDC__ //如果编译遵循ANSI C，其值为1，否则未定义
// 
// printf("file:%s line:%d\n",__FILE__,__LINE__);
// //例如：打印当前源文件名（或路径）和行号
// 
// 2.#define定义常量
// （可理解为替换）
// #define name stuff
//    定义对象名 定义为：
// #define M 100+2
// #define str "hehe"
// #define foever for(;;)   
//  //==  for( ; ; )     for循环的判断部分（中间）什么都不放：代表恒成立（死循环）
//  //经过定义后，forever;  可以替换上面的for循环
// 
// //如果定义的stuff过长，可以分成几行写，除了最后一行，每行的末尾都要加上一个反斜杠（续行符）。
// #define DEBUG_PRINT printf("file:%s\tline:%d\t \
//                           date:%s\ttime:%s\n" ,\
//                           __FILE__,__LINE__,   \
//                           __DATE__,__TIME__)
// 
// 3.#define定义宏
// 宏的声明方式：
// #definen name(narament-list) stuff
//               参数（narament-list是一个有逗号隔开的符号表，他们可能出现在stuff中）
// ！！！参数列表的左括号必须与name紧接，如果有空白，参数列表就会被视为stuff的部分
//  例如：定义宏来实现平方
/*
#define SQUARE(X) （（X）*（X））
//参数是X，替换的内容是参数的平方
//定义宏的时候一定要考虑到参数可能是一个表达式，替换后，由于符号的优先，可能影响到计算结果
//宏在书写时，尽量都带上括号
int main()
{
	int a = 5;
	printf("%d\n", SQUARE(a));
	return 0;
}
*/

// 4.带有副作用的宏函数
// 当宏参数在宏的定义中出现超过一次时，如果参数带有副作用，那么在使用宏的时候就可能出现错误，副作用就是表达式求值的时候出现的永久性效果
// 例如：X+1；不带副作用，X++带有副作用
/*
#define MAX(a,b) ((a)>(b)?(a):(b))
...
x = 5;
y = 8;
z = MAX(x++,y++);
printf("x=%d y=%d z=%d\n", x, y, z);
*/

// 5.宏替换的规则
// 在调用宏的时候，首先对参数进行检查，看看是否包含任何由#define定义的符号，如果是，他们首先被替换
// 替换文本随后被插入到的程序中原来的文本位置。对于宏，参数名被他们的值所替换。
// 最后，再次对文件进行扫描，看看它是否包含任何#define定义的符号。如果是就你重复上述的步骤
// ！！！
// 宏参数和#define定义中可以出现其他#define定义的符号，但是对于宏不能出现递归
// 当预处理器搜索#define定义的符号时，字符串常量的内容并不被搜索
// 6.宏函数的对比
/*
#define M 10
//宏
#define MAX(a,b) ((a)>(b)?(a):(b))
//函数
int Max(int x, int y)
{
	return x > y ? x: y;
}
int main()
{
	int m = MAX(M, 3 + 5);
	printf("m=%d\n", m);
	int n = Max(M, 3 + 5);
	printf("n=%d\n", n);
	return 0;
}
*/
//宏和函数的执行结果一致，但各有优劣
// 宏优点：
// 调用函数有时候耗费时间更多，宏在速度方面更胜一筹
// 函数的参数必须声明为特定的类型。所以函数只能再适合的类型的表达式上使用。宏的参数是与类型无关的。
// 
// 缺点：除非宏比较短，否则大幅增加程序的长度。
//      宏是无法调试的。
//      宏与类型无关，也就不够严谨
//      宏可能会带来运算符优先级的问题，导致容易出现错误。
// 
// 7.#和##
// 
// //头文件的包含
// #include
// //符号和宏定义
// #define 
// 
// #运算符将宏的一个参数转换为字符串字面量。他仅允许出现在带参数的宏的替换列表中。
//#define PRINT(n) printf("the value of"#n"is %d",n);
// PRINT(a);
// //printf("the value of""a""is %d",a);
// 上面这个定义可以将变量给替换到宏的体内（#n就是转换为"a"这个字符串）
//
// ##连接两个字符串（必须生成一个合法的标识符）
//使用宏，定义一个通用的函数模具
/*
#define MAX(type) \                           自定义函数引用时的名字和参数
type type##_max(type x,type y)\               函数的内容（关于参数）
{\
    return (x>y?x:y);\
}\

MAX(int)
//引用时放入type即可生成对应类型的函数

//int int_max(int x, int y)\
//{\
//return (x > y ? x : y); \
//}\

MAX(float)
MAX(double)

//使用
int main()
{
	int m = int_max(3, 5);
	printf("%d\n",m);
	return 0;
}
*/
// 8.命令约定
//一般来说函数的宏的使用语法很相似。所以语言本身没办法帮我们区分二者。所以我们一般的习惯是：
//把宏名全部大写        MAX() ...
//函数名不要全部大写    Max(int a,int b)
//！！！但也有例外
// 9.#undef
//这条指令用于移除一条宏定义
/*
#define MAX 100
int main()
{
	printf("%d\n",MAX);
#undef MAX//这之后MAX这个宏定义被移除
	//但后续还可以继续重新定义
#define MAX 10086
	printf("%d\n", MAX);
	return 0;
}
*/
// 10.命令行定义
// 许多c的编译器提供了一种能力，允许在命令行中定义符号。用于启动编译过程
// 不改动代码的情况下，可以实现一个源文件编译出一个程序的不同版本。
// linux环境下：  gcc -d  ARRAY_SIZE=10  program.c




//  11.条件编译指令
// #if
// #ifdef   ABC
// #ifndef  ABC
// #endif   //判断结束语
// #if defined (ABC)
// #if !define (ABC)
// 适用情况：如果程序中有一些调试性的代码，删除有可能下次会使用，保留又影响原程序，由此我们可以进行选择性编译
/*
#define __DEBUG__
int main()
{
	int i = 0;
	int arr[10] = { 0 };
	for (i = 0; i < 10; i++)
	{
		arr[i] = i;
#ifdef __DEBUG__//如果__DEBUG__已经定义了，则判断正确，执行下面的代码
		printf("%d\n", arr[i]);//为了观察数组是否赋值成功
#endif //__DEBUG__
	}
	//这段代码中#ifdef和上面的宏形成对186行代码的开关作用
	return 0;
}
*/
//条件编译指令
// 1.
// #if  常量表达式(此表达式结果为真，则...参与编译)
//  //...
// #endif
// //常量表达式由预处理器求值
// 如：
// #define __DEBUG__ 1    //把__DEBUG__替换为1
// #if __DEBUG__          //这里值为1，为真
//      //...
// #endif
// 2.多个分支的条件编译
// #if 常量表达式           //第一种情况
//       //.....
// #elif 常量表达式         //第二种情况（第一种不行就进入第二种情况：else if（简写为elif））
//       //.....
// #else                   //第三种情况
//       //.....
// #endif
// 
// 3.判断是否被定义
// #if defined(symbol)    |这两种方法都是判断
// #ifdef symbol          |是否symbol已经被定义
// //....
// #if !defined(symbol)    |这两种方法都是判断  
// #ifndef symbol          |是否symbol未被定义
// //....
// #endif
// 4.嵌套指令
// 
//#if defined(OS_UNIX)       //如果定义了OS_UNIX
//      #ifdef OPTION1       //且定义了OPTION1
//          plan1();         //则执行plan1这个函数
//      #endif
//      #ifdef OPTION2
//          plan2();
//      #endif
//#elif defined(OS_MSDOS)       //如果是定义了OS_MSDOS的情况
//      #ifdef OPTION2          //也定义了OPTION2
//          plan3();            //则执行plan3这个函数
//      #endif
//#endif

// 12.头文件的包含
// #include <stdio.h>   //库文件包含
// 直接去标准路径下寻找，找不到则报错
// #include "filename.h"  //本地文件的包含
// 先在源文件所在目录下查找，如果该头文件未找到，编译器就像查找库函数的头文件一样，在标准位置查找头文件
// 如果找不到就提示编译错误❌
// //其实库文件也可以用""包含，不过这样查找效率会变低，也不容易区分式库文件还是本地文件了
// 
// #include "name.h"  //包含一个自定义的函数声明的头文件
// //在预处理的时候，预处理器会将函数内容（头文件信息）给拷贝到程序上面
// //如果重复包含，则也会重复编译，则会造成代码量，编译的压力较大的情况
//
//  ！！！这时，就要用上条件编译了
// //在需要的头文件上，做一些处理
// #ifndef __TEST__
// #define __TEST__
// 
// int Add(int x,int y);
// #endif
// 
// //这样之后，如果出现过一次上面的内容，下一次再出现，
// //#ifndef 判断的结果为假（因为已经定义过了），后续的内容都不会被执行
// 
// //#pragma once
// // int Add(int x,int y);
// //这样也可以避免头文件重复多次包含
// 
// 13.其他预处理指示
//typedef 类型 别名
// typederf int Dtype ;//
// Dtype a;//int a;
// //!!!注意别漏掉；
// 

//链接
//










































































































































































































































